from zlgcan import *
import threading
import time
import sys
import binascii
import signal
import argparse
from enum import Enum
from typing import Any


class Steps(Enum):
    upgrade_check = 1
    enter_boot_mode = 2
    erase_flash = 3
    data_begin = 4
    data_transfer = 5
    data_end = 6
    data_CRC = 7
    reboot = 8
    finished = 9


send_dict = {
    Steps.upgrade_check: bytes([1, 1]),
    Steps.enter_boot_mode: bytes([0x10, 0x02]),
    Steps.erase_flash: bytes([0x31]),
    Steps.data_begin: bytes([0x34]),
    Steps.data_transfer: bytes([0x36]),
    Steps.data_end: bytes([0x37]),
    Steps.data_CRC: bytes([0x02]),
    Steps.reboot: bytes([0x11, 0x01]),
}
recv_dict = {
    Steps.upgrade_check: [0x41, 1],
    Steps.enter_boot_mode: [0x50, 0x02],
    Steps.erase_flash: [0x71],
    Steps.data_begin: [0x74],
    Steps.data_transfer: [0x76],
    Steps.data_end: [0x77],
    Steps.data_CRC: [0x42],
    Steps.reboot: [0x51, 0x01],
}

# 定义一些全局变量
upgrade_context_dict = {
    "cpu1_erase_flash": 0x00088000,  # cpu1擦除flash地址
    "cpu2_erase_flash": 0x00086000,  # cpu2擦除flash地址
    "dl_can_id": 0x18000088,
    "ul_can_id": 0x18000080,
    "timeout_ms": 500,  # 超时时间
    "frame_interval": 0.001,  # CAN帧发送间隔（秒），0表示无间隔
    "send_recv_status": "",  # 取值为'resp_OK','req_send'
    "upgrade_step": "",
    "send_block_size": 0,
    "send_block_SN": 0,
    "recv_block_index": 0,
    "send_time": "",  # time.time()*1000,精度到毫秒
    "max_retry_count": 3,  # 最大重传次数
    "current_retry_count": 0,  # 当前重传次数
}

# 全局日志文件名
log_filename = None

# 全局CAN资源变量（用于信号处理器访问）
global_can_resources: dict[str, Any] = {
    "zcanlib": None,           # ZCAN库实例
    "device_handle": None,     # 设备句柄
    "chn_handle": None         # 通道句柄
}


# 打印CAN帧的candump格式函数
def print_can_frame(can_id, dlc, data, direction="TX"):
    """
    打印CAN帧，模仿candump格式，并同步保存到日志文件
    direction: "TX" 表示发送, "RX" 表示接收
    """
    global log_filename
    
    timestamp = (
        time.strftime("%Y-%m-%d %H:%M:%S")
        + f".{int((time.time() % 1) * 1_000_000):06d}"
    )
    can_id_hex = f"{can_id:08X}"
    data_hex = " ".join(f"{b:02X}" for b in data[:dlc])
    log_message = f"({timestamp})  {direction}  {can_id_hex}   [{dlc}]  {data_hex}"
    
    # 打印到控制台
    print(log_message)
    
    # 同步保存到日志文件
    if log_filename:
        try:
            import os
            log_file_path = os.path.join(os.getcwd(), log_filename)
            with open(log_file_path, "a", encoding="utf-8") as log_file:
                log_file.write(log_message + "\n")
                log_file.flush()  # 强制刷新缓冲区，确保立即写入
        except Exception as e:
            print(f"写入日志文件失败: {e}")
            print(f"当前工作目录: {os.getcwd()}")
            print(f"尝试写入的日志文件路径: {log_file_path}")


# 读取设备信息
def Read_Device_Info(device_handle):

    # 获取设备信息
    info = zcanlib.GetDeviceInf(device_handle)
    if info == None:
        sys.exit("获取设备信息失败！")
    print("设备信息: \n%s" % info)

    can_number = info.can_num
    return can_number


# 启动通道
def USBCANEU_Start(
    zcanlib, device_handle, chn, baud_rate="500000", enable_filter=False
):

    # 波特率设置
    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/baud_rate", baud_rate.encode("utf-8")
    )
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d baud rate %s failed!" % (chn, baud_rate))
        return None

    # 自定义波特率    当产品波特率对采样点有要求，或者需要设置非常规波特率时使用   ---默认不管
    # ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "//baud_rate_custom", "500Kbps(80%),2.0Mbps(80%),(80,07C00002,01C00002)".encode("utf-8"))
    # if ret != ZCAN_STATUS_OK:
    #     print("Set CH%d baud failed!" % chn)
    #     return None

    # 初始化通道
    chn_init_cfg = ZCAN_CHANNEL_INIT_CONFIG()
    chn_init_cfg.can_type = ZCAN_TYPE_CAN  # USBCAN 必须选择CAN
    chn_init_cfg.config.canfd.mode = 0  # 0-正常模式 1-只听模式

    chn_init_cfg.config.can.acc_mask = 0xFFFFFFFF
    chn_init_cfg.config.can.mode = 0

    chn_handle = zcanlib.InitCAN(device_handle, chn, chn_init_cfg)
    if chn_handle is None:
        print("initCAN failed!" % chn)
        return None

    # 设置滤波  USBCAN-8E-U无法通过此函数设置滤波
    if enable_filter:
        Set_Filter(device_handle, chn)

    # 启动通道
    ret = zcanlib.StartCAN(chn_handle)
    if ret != ZCAN_STATUS_OK:
        print("startCAN failed!" % chn)
        return None

    return chn_handle


# 设置滤波  白名单过滤(只接收范围内的数据)    USBCAN-E设备支持设置最多32组滤波   8E-U不支持此配置
def Set_Filter(device_handle, chn):

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_clear", "0".encode("utf-8")
    )  # 清除滤波
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_clear failed!" % chn)
        return None

    # 流程为：for【set_mode(与前一组滤波同类型可以省略) + set_start + set_end】+ ack
    # 这里设置第一道滤波：标准帧0~0x7F，第二道滤波：标准帧0xFF~0x1FF，第三道滤波：扩展帧0xFF~0x2FF
    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_mode", "0".encode("utf-8")
    )  # 设置滤波模式 标准帧滤波
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_mode failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_start", "0".encode("utf-8")
    )  # 设置白名单范围 起始ID
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_start failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_end", "0x7F".encode("utf-8")
    )  # 设置白名单范围 结束ID
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_end failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_start", "0xFF".encode("utf-8")
    )
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_start failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_end", "0x1FF".encode("utf-8")
    )
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_end failed!" % chn)
        return None

    # 扩展帧滤波
    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_mode", "1".encode("utf-8")
    )
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_mode failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_start", "0xFF".encode("utf-8")
    )
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_start failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_end", "0x2FF".encode("utf-8")
    )
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_end failed!" % chn)
        return None

    ret = zcanlib.ZCAN_SetValue(
        device_handle, str(chn) + "/filter_ack", "0".encode("utf-8")
    )  # 使能滤波
    if ret != ZCAN_STATUS_OK:
        print("Set CH%d  filter_ack failed!" % chn)
        return None


def send_predefine_frame(can_handle: int):
    transmit_num = 1
    msgs = (ZCAN_Transmit_Data * transmit_num)()
    msgs[0].transmit_type = 0  # 0-正常发送，2-自发自收
    msgs[0].frame.eff = 1  # 0-标准帧，1-扩展帧
    msgs[0].frame.rtr = 0  # 0-数据帧，1-远程帧
    msgs[0].frame.can_id = 0x1FF01
    msgs[0].frame.can_dlc = 8
    msgs[0].frame.data[0:8] = bytes([2, 3, 4, 5, 6, 7, 8, 9])
    # 打印发送的帧
    print_can_frame(msgs[0].frame.can_id, msgs[0].frame.can_dlc, msgs[0].frame.data)

    ret = zcanlib.Transmit(can_handle, msgs, transmit_num)
    if ret != 1:
        print(f"Transmit failed: {ret}")


def send_command(can_handle: int, dsp_OTA_can_id: c_uint, send_bytes: bytes):
    if upgrade_context_dict["send_recv_status"] == "req_send":
        print("send_recv_status is req,will not send command!")
        cleanup_can_resources()
        sys.exit(1)
    
    # 添加重传信息到日志
    retry_info = ""
    if upgrade_context_dict["current_retry_count"] > 0:
        retry_info = f" [重传 {upgrade_context_dict['current_retry_count']}/{upgrade_context_dict['max_retry_count']}]"
    print(f"发送命令{retry_info}: {' '.join(f'{b:02X}' for b in send_bytes)}")

    # ZCAN_Transmit_Data结构体说明：
    # 该结构体用于描述要通过CAN总线发送的数据帧，包含两个主要成员：
    # 1. frame：类型为ZCAN_CAN_FRAME，表示具体的CAN帧内容（如ID、数据长度、数据等）。
    #    - ZCAN_CAN_FRAME结构体字段说明：
    #      * can_id：CAN帧ID（支持标准帧和扩展帧，29位）。
    #      * err：错误帧标志位。
    #      * rtr：远程帧标志位（0-数据帧，1-远程帧）。
    #      * eff：扩展帧标志位（0-标准帧，1-扩展帧）。
    #      * can_dlc：数据长度（0~8）。
    #      * data：实际要发送的数据内容（最多8字节）。
    # 2. transmit_type：发送类型，0表示正常发送，2表示自发自收等模式。
    # 该结构体通常用于组包后批量发送CAN帧。

    send_length = len(send_bytes)
    if send_length < 8:  # 单帧发送
        transmit_num = 1

        # 创建一个长度为transmit_num的ZCAN_Transmit_Data结构体数组
        # (ZCAN_Transmit_Data * transmit_num)() 是ctypes的用法，等价于C语言中的 ZCAN_Transmit_Data msgs[transmit_num];
        msgs = (ZCAN_Transmit_Data * transmit_num)()

        msgs[0].transmit_type = 0  # 0-正常发送，2-自发自收
        msgs[0].frame.eff = 1  # 0-标准帧，1-扩展帧
        msgs[0].frame.rtr = 0  # 0-数据帧，1-远程帧
        msgs[0].frame.can_id = dsp_OTA_can_id
        msgs[0].frame.can_dlc = send_length + 1
        msgs[0].frame.data[0] = send_length
        msgs[0].frame.data[1 : send_length + 1] = send_bytes

        # 打印发送的帧
        print_can_frame(msgs[0].frame.can_id, msgs[0].frame.can_dlc, msgs[0].frame.data)

        ret = zcanlib.Transmit(can_handle, msgs, transmit_num)
        if ret != 1:
            print(f"Transmit failed: {ret}")
    else:
        a, b = divmod((send_length - 5), 7)
        transmit_num = a + 2
        rollcount = 1
        msgs = (ZCAN_Transmit_Data * transmit_num)()
        memset(addressof(msgs), 0, sizeof(msgs))
        msgs[0].transmit_type = 0  # 0-正常发送，2-自发自收
        msgs[0].frame.eff = 1  # 0-标准帧，1-扩展帧
        msgs[0].frame.rtr = 0  # 0-数据帧，1-远程帧
        msgs[0].frame.can_id = dsp_OTA_can_id
        # msgs[0].frame.can_dlc = send_length + 1
        msgs[0].frame.can_dlc = 8
        msgs[0].frame.data[0] = 0x40
        msgs[0].frame.data[1:3] = send_length.to_bytes(2, "big")
        msgs[0].frame.data[3:8] = send_bytes[:5]
        remaining_len = send_length - 5
        for i in range(1, transmit_num):
            msgs[i].transmit_type = 0  # 0-正常发送，2-自发自收
            msgs[i].frame.eff = 1  # 0-标准帧，1-扩展帧
            msgs[i].frame.rtr = 0  # 0-数据帧，1-远程帧
            msgs[i].frame.can_id = dsp_OTA_can_id
            # 计算当前帧应该发送的数据长度
            current_frame_data_len = min(7, remaining_len - 7 * (i - 1))
            msgs[i].frame.can_dlc = current_frame_data_len + 1
            msgs[i].frame.data[0] = 0x80 + rollcount
            rollcount += 1
            rollcount %= 4
            msgs[i].frame.data[1 : current_frame_data_len + 1] = send_bytes[
                5 + 7 * (i - 1) : 5 + 7 * (i - 1) + current_frame_data_len
            ]
        # 按照指定间隔，逐帧发送
        for i in range(transmit_num):
            # 打印发送的帧
            print_can_frame(
                msgs[i].frame.can_id, msgs[i].frame.can_dlc, msgs[i].frame.data
            )

            ret = zcanlib.Transmit(can_handle, msgs[i], 1)
            if ret != 1:
                print(f"Transmit failed: {ret}")
            time.sleep(upgrade_context_dict["frame_interval"])

        print("transmit_num:", transmit_num)
        upgrade_context_dict["send_time"] = time.time() * 1000  # 精度到毫秒


def recv_can_frame(chn_handle: int, dsp_OTA_can_id: c_uint):
    while True:
        rcv_num = zcanlib.GetReceiveNum(
            chn_handle, ZCAN_TYPE_CAN
        )  # 返回值为接收缓存里面的CAN帧数
        if rcv_num:
            rcv_msg, rcv_num = zcanlib.Receive(chn_handle, rcv_num)
            # ZCAN_Receive_Data结构体说明：
            # 该结构体用于表示接收到的CAN帧数据，通常包含以下成员：
            # - frame: ZCAN_CAN_FRAME类型，表示CAN帧的具体内容（如ID、数据、帧类型等）
            #     结构体ZCAN_CAN_FRAME说明：
            #     - can_id: CAN帧ID（支持标准帧和扩展帧）
            #     - eff: 是否为扩展帧标志（0-标准帧，1-扩展帧）
            #     - rtr: 是否为远程帧标志（0-数据帧，1-远程帧）
            #     - can_dlc: 数据长度（Data Length Code，0~8）
            #     - data: 实际的数据内容（最多8字节）
            # - timestamp: 接收该帧时的时间戳（单位通常为微秒或毫秒，具体取决于驱动实现）
            # 在调用zcanlib.Receive()函数时，返回的就是ZCAN_Receive_Data结构体数组。
            for i in range(rcv_num):
                # 打印接收的帧
                print_can_frame(
                    rcv_msg[i].frame.can_id,
                    rcv_msg[i].frame.can_dlc,
                    rcv_msg[i].frame.data,
                    "RX",
                )

                if rcv_msg[i].frame.can_id == dsp_OTA_can_id:
                    print("!!!!!!!!!!!!!!!!!")
                    parse_can_frame_data(rcv_msg[i].frame.data)

                if rcv_msg[i].frame.data[1] == 0x7F:
                    negetive_resp_cmd = rcv_msg[i].frame.data[2]
                    tmp_step: Steps = upgrade_context_dict["upgrade_step"]
                    tmp_send_cmd = send_dict[tmp_step][0]
                    if (
                        negetive_resp_cmd == tmp_send_cmd
                        and rcv_msg[i].frame.data[3] == 0x78
                    ):
                        print(
                            f"receive negetive resp: {rcv_msg[i].frame.data},will restart timeout timer!"
                        )
                        upgrade_context_dict["send_time"] = time.time() * 1000


def parse_can_frame_data(data_array: bytes):
    current_step: Steps = upgrade_context_dict["upgrade_step"]

    # 其他步骤解析
    if (
        data_array[0] == len(recv_dict[current_step])
        and data_array[1 : len(recv_dict[current_step]) + 1] == recv_dict[current_step]
    ):
        upgrade_context_dict["send_recv_status"] = "resp_OK"
        next_step = Steps(current_step.value + 1)
        upgrade_context_dict["upgrade_step"] = next_step

    # 数据块开始步骤解析
    if (
        data_array[0]
        == len(recv_dict[Steps.data_begin])
        + 2  # +2是因为data_begin的命令长度为1，但是返回的命令长度为3:cmd1字节+返回的分块长度2字节
        and data_array[1 : len(recv_dict[Steps.data_begin]) + 1]
        == recv_dict[Steps.data_begin]
    ):
        upgrade_context_dict["send_block_size"] = int.from_bytes(data_array[2:4], "big")
        upgrade_context_dict["send_recv_status"] = "resp_OK"
        next_step = Steps(current_step.value + 1)
        upgrade_context_dict["upgrade_step"] = next_step

    # 数据块传输步骤解析
    if (
        data_array[0]
        == len(recv_dict[Steps.data_transfer])
        + 1  # +1是因为data_transfer的命令长度为1，但是返回的命令长度为2:cmd1字节+返回的分块序号1字节
        and data_array[1 : len(recv_dict[Steps.data_transfer]) + 1]
        == recv_dict[Steps.data_transfer]
    ):
        block_SN = data_array[2]
        print(f"block_SN: {block_SN}")
        print(
            f"upgrade_context_dict['send_block_SN']: {upgrade_context_dict['send_block_SN']}"
        )
        if block_SN == upgrade_context_dict["send_block_SN"]:
            upgrade_context_dict["send_recv_status"] = "resp_OK"


def hex_to_bin_bytes(hex_file: str) -> bytes:
    FILL_VALUE = 0xFF
    blocks = []
    base_address = 0
    min_address = 0xFFFFFFFF
    max_address = 0

    with open(hex_file, "r") as f:
        for line in f:
            line = line.strip()
            if not line.startswith(":"):
                continue

            byte_count = int(line[1:3], 16)
            address = int(line[3:7], 16)
            record_type = int(line[7:9], 16)

            if record_type == 0x02 or record_type == 0x04:
                new_base = int(line[9:13], 16)
                base_address = (
                    (new_base << 4) if record_type == 0x02 else (new_base << 16)
                )

            elif record_type == 0x00:
                current_start_byte = (base_address + address) * 2
                current_end_byte = current_start_byte + byte_count - 1

                min_address = min(min_address, current_start_byte)
                max_address = max(max_address, current_end_byte)

                data = bytes(
                    int(line[9 + i * 2 : 11 + i * 2], 16) for i in range(byte_count)
                )
                blocks.append((current_start_byte, data))

    if not blocks:
        print("No data records found in HEX file.")
        return b""

    bin_size = max_address - min_address + 1
    bin_data = bytearray([FILL_VALUE] * bin_size)

    for start, data in blocks:
        offset = start - min_address
        bin_data[offset : offset + len(data)] = data

    print("Conversion successful with 0xFF padding!")
    print(f"HEX range: 0x{min_address:08X} - 0x{max_address:08X}")
    print(f"BIN size: {bin_size} bytes (0x{bin_size:08X})")

    return bytes(bin_data)


def hex_to_bin_file(hex_file: str, bin_file: str) -> int:
    FILL_VALUE = 0xFF
    blocks = []
    base_address = 0
    min_address = 0xFFFFFFFF
    max_address = 0

    with open(hex_file, "r") as f:
        for line in f:
            line = line.strip()
            if not line.startswith(":"):
                continue

            byte_count = int(line[1:3], 16)
            address = int(line[3:7], 16)
            record_type = int(line[7:9], 16)

            if record_type == 0x02 or record_type == 0x04:
                new_base = int(line[9:13], 16)
                base_address = (
                    (new_base << 4) if record_type == 0x02 else (new_base << 16)
                )

            elif record_type == 0x00:
                current_start_byte = (base_address + address) * 2
                current_end_byte = current_start_byte + byte_count - 1

                min_address = min(min_address, current_start_byte)
                max_address = max(max_address, current_end_byte)

                data = bytes(
                    int(line[9 + i * 2 : 11 + i * 2], 16) for i in range(byte_count)
                )
                blocks.append((current_start_byte, data))

    if not blocks:
        print("No data records found in HEX file.")
        return -1

    bin_size = max_address - min_address + 1
    bin_data = bytearray([FILL_VALUE] * bin_size)

    for start, data in blocks:
        offset = start - min_address
        bin_data[offset : offset + len(data)] = data

    with open(bin_file, "wb") as f:
        f.write(bin_data)

    print("Conversion successful with 0xFF padding!")
    print(f"HEX range: 0x{min_address:08X} - 0x{max_address:08X}")
    print(f"BIN size: {bin_size} bytes (0x{bin_size:08X})")
    print(f"Binary file saved to: {bin_file}")

    return 0


def cleanup_can_resources():
    """
    清理CAN资源的简单函数
    检查并关闭通道和设备
    使用全局变量访问资源
    
    使用示例:
        cleanup_can_resources()
    """
    global global_can_resources
    
    try:
        zcanlib = global_can_resources["zcanlib"]
        device_handle = global_can_resources["device_handle"]
        chn_handle = global_can_resources["chn_handle"]
        
        print("开始清理CAN资源...")
    except Exception as e:
        print(f"获取全局资源时发生异常: {e}")
        return
    
    # 1. 检查并关闭CAN通道
    if chn_handle is not None and zcanlib is not None:
        try:
            print("正在关闭CAN通道...")
            ret = zcanlib.ResetCAN(chn_handle)
            if ret != ZCAN_STATUS_OK:
                print(f"关闭CAN通道失败: {ret}")
            else:
                print("CAN通道已关闭")
        except Exception as e:
            print(f"关闭CAN通道时发生异常: {e}")
    else:
        print("CAN通道未打开，无需关闭")
    
    # 2. 检查并关闭CAN设备
    if device_handle is not None and device_handle != INVALID_DEVICE_HANDLE and zcanlib is not None:
        try:
            print("正在关闭CAN设备...")
            ret = zcanlib.CloseDevice(device_handle)
            if ret != ZCAN_STATUS_OK:
                print(f"关闭CAN设备失败: {ret}")
            else:
                print("CAN设备已关闭")
        except Exception as e:
            print(f"关闭CAN设备时发生异常: {e}")
    else:
        print("CAN设备未打开，无需关闭")
    
    print("CAN资源清理完成，886!")


def signal_handler(signum, frame):
    """信号处理器，用于优雅退出"""
    
    print(f"\n收到信号 {signum}，程序正在退出...")
    print("正在清理资源...")
    
    # 清理CAN资源
    try:
        cleanup_can_resources()
        print("资源清理完成")
    except Exception as e:
        print(f"清理资源时发生异常: {e}")
    
    print("程序退出")
    sys.stdout.flush()  # 强制刷新输出缓冲区
    sys.exit(0)


def wait_response():
    """等待响应，支持重传机制"""
    start_time = time.time()
    while (time.time() - start_time) * 1000 < upgrade_context_dict["timeout_ms"]:
        if upgrade_context_dict["send_recv_status"] == "resp_OK":
            # 收到正确响应，重置重传计数
            upgrade_context_dict["current_retry_count"] = 0
            return "resp_OK"
        time.sleep(0.01)
    
    # 超时，增加重传计数
    upgrade_context_dict["current_retry_count"] += 1
    print(f"等待响应超时，当前重传次数: {upgrade_context_dict['current_retry_count']}/{upgrade_context_dict['max_retry_count']}")
    
    if upgrade_context_dict["current_retry_count"] >= upgrade_context_dict["max_retry_count"]:
        print(f"达到最大重传次数 {upgrade_context_dict['max_retry_count']}，升级失败")
        cleanup_can_resources()
        sys.exit(1)
    
    return "timeout"


def send_command_with_retry(can_handle: int, dsp_OTA_can_id: c_uint, send_bytes: bytes, step_name: str):
    """带重传机制的发送命令函数"""
    # 每个新步骤开始时重置重传计数
    upgrade_context_dict["current_retry_count"] = 0
    
    while True:
        # 重置发送状态
        upgrade_context_dict["send_recv_status"] = ""
        
        # 发送命令
        send_command(can_handle, dsp_OTA_can_id, send_bytes)
        upgrade_context_dict["send_recv_status"] = "req_send"
        
        # 等待响应
        ret = wait_response()
        
        if ret == "resp_OK":
            print(f"{step_name}完成")
            return True
        elif ret == "timeout":
            print(f"{step_name}超时，准备重传...")
            time.sleep(0.1)  # 重传前短暂等待
            continue
        else:
            print(f"{step_name}失败")
            return False


if __name__ == "__main__":
    # 解析命令行参数
    parser = argparse.ArgumentParser(description="CAN OTA升级工具")
    parser.add_argument("can_channel", type=int, choices=[0, 1], help="CAN通道号 (0 或 1)")
    parser.add_argument("cpu_id", type=int, choices=[1, 2], help="CPU ID (1 或 2)")
    parser.add_argument("hex_file", type=str, help="HEX文件路径")
    parser.add_argument("--max-retry", type=int, default=3, help="最大重传次数 (默认: 3)")

    args = parser.parse_args()
    
    # 设置最大重传次数
    upgrade_context_dict["max_retry_count"] = args.max_retry
    print(f"最大重传次数设置为: {upgrade_context_dict['max_retry_count']}")
    
    # 初始化日志文件名（程序开始时生成一次）
    log_filename = f"OTA_{time.strftime('%Y%m%d_%H%M%S')}.log"
    print(f"日志文件: {log_filename}")
    
    # 根据CPU ID设置相应的配置
    if args.cpu_id == 1:
        flash_addr = upgrade_context_dict["cpu1_erase_flash"]
        print(f"选择CPU1，擦除地址: 0x{flash_addr:08X}")
    else:
        flash_addr = upgrade_context_dict["cpu2_erase_flash"]
        print(f"选择CPU2，擦除地址: 0x{flash_addr:08X}")

    print(f"HEX文件路径: {args.hex_file}")

    # 注册信号处理器，用于优雅退出
    signal.signal(signal.SIGINT, signal_handler)  # Ctrl+C
    signal.signal(signal.SIGTERM, signal_handler)  # 终止信号
    

    zcanlib = ZCAN()
    handle = zcanlib.OpenDevice(ZCAN_USBCAN_2E_U, 0, 0)
    if handle == INVALID_DEVICE_HANDLE:
        sys.exit("Open USBCAN-XE-U device failed!")
    print("Open USBCAN-XE-U device success! device handle:%d." % (handle))
    
    # 设置全局资源变量（用于信号处理器访问）
    global_can_resources["zcanlib"] = zcanlib
    global_can_resources["device_handle"] = handle

    # # 获取设备信息
    # can_number = Read_Device_Info(handle)

    # 启动指定通道
    selected_channel = args.can_channel
    baud_rate = "500000"  # 可以修改为其他波特率，如 "250000", "1000000" 等
    enable_filter = False  # True-启用滤波，False-禁用滤波
    chn_handle = USBCANEU_Start(zcanlib, handle, selected_channel, baud_rate, enable_filter)
    if chn_handle is None:
        sys.exit("启动通道%d失败！" % selected_channel)
    print(
        "通道: %d, 通道句柄: %d, 波特率: %s, 滤波: %s."
        % (selected_channel, chn_handle, baud_rate, "启用" if enable_filter else "禁用")
    )
    
    # 设置通道句柄到全局变量
    global_can_resources["chn_handle"] = chn_handle

    sys.stdin.read(1)
    #########################################################################

    dsp_OTA_can_id = upgrade_context_dict["ul_can_id"]
    SCU_OTA_can_id = upgrade_context_dict["dl_can_id"]

    # print(send_dict["erase_flash"].hex())
    # print(" ".join(f"{b:02X}" for b in send_dict["erase_flash"]))

    # hex文件转bin字节流
    byte_stream: bytes = hex_to_bin_bytes(args.hex_file)
    bin_size = len(byte_stream)
    send_dict[Steps.erase_flash] = bytes(
        send_dict[Steps.erase_flash]
        + flash_addr.to_bytes(4, "big")
        + bin_size.to_bytes(4, "big")
    )
    send_dict[Steps.data_begin] = bytes(
        send_dict[Steps.data_begin]
        + flash_addr.to_bytes(4, "big")
        + bin_size.to_bytes(4, "big")
    )

    crc = binascii.crc32(byte_stream) & 0xFFFFFFFF
    send_dict[Steps.data_CRC] = bytes(
        send_dict[Steps.data_CRC] + crc.to_bytes(4, "big")
    )

    # print(' '.join(f'{b:02X}' for b in send_dict["erase_flash"]))
    # # print(f"CRC32: 0x{crc:08X}")

    # 启动接收线程
    recv_thread = threading.Thread(
        target=recv_can_frame, args=(chn_handle, SCU_OTA_can_id)
    )
    recv_thread.daemon = True  # 设置为守护线程，主程序结束时自动退出
    recv_thread.start()

    # 发送前置条件帧
    send_predefine_frame(chn_handle)
    time.sleep(1)
    # ret = wait_response()
    # 升级条件检查
    print("开始升级条件检查")
    # 只有第一步设置当前步骤，后续步骤通过解析CAN帧数据设置
    upgrade_context_dict["upgrade_step"] = Steps.upgrade_check
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "升级条件检查"
    )
    sys.stdin.read(1)
    # 进入boot模式
    print("开始进入boot模式")
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "进入boot模式"
    )
    sys.stdin.read(1)
    # 擦除flash
    print("开始擦除flash")
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "擦除flash"
    )
    sys.stdin.read(1)
    # 数据块下载
    print("开始数据块下载")
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "数据块下载"
    )
    sys.stdin.read(1)

    # 数据传输
    print("开始数据传输")
    block_start = 1
    read_offset = 0
    read_size = upgrade_context_dict["send_block_size"] - 2
    while read_offset < bin_size:
        if read_offset + read_size > bin_size:
            read_size = bin_size - read_offset
        read_bytes = byte_stream[read_offset : read_offset + read_size]
        read_offset += read_size

        total_bytes = (
            send_dict[Steps.data_transfer] + block_start.to_bytes(1, "big") + read_bytes
        )
        
        # 为每个数据块使用重传机制
        # 每个新数据块开始时重置重传计数
        upgrade_context_dict["current_retry_count"] = 0
        
        while True:
            # 重置发送状态
            upgrade_context_dict["send_recv_status"] = ""
            
            # 发送数据块
            send_command(chn_handle, dsp_OTA_can_id, total_bytes)
            upgrade_context_dict["send_block_SN"] = block_start
            upgrade_context_dict["send_recv_status"] = "req_send"
            
            # 等待响应
            ret = wait_response()
            
            if ret == "resp_OK":
                print(f"数据块 {block_start} 传输完成")
                break
            elif ret == "timeout":
                print(f"数据块 {block_start} 传输超时，准备重传...")
                time.sleep(0.1)  # 重传前短暂等待
                continue
            else:
                print(f"数据块 {block_start} 传输失败")
                break
        
        block_start += 1
    upgrade_context_dict["upgrade_step"] = Steps.data_end

    # 数据块结束
    sys.stdin.read(1)
    print("开始数据块结束")
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "数据块结束"
    )
    sys.stdin.read(1)
    # 数据CRC
    print("开始数据CRC")
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "数据CRC"
    )
    sys.stdin.read(1)
    # 重启
    print("开始重启")
    send_command_with_retry(
        chn_handle, dsp_OTA_can_id, send_dict[upgrade_context_dict["upgrade_step"]], "重启"
    )

    # 程序结束前的清理工作
    print("OTA升级完成，程序即将退出...")
    time.sleep(1)  # 给接收线程一点时间处理最后的消息
    
    # 清理CAN资源
    cleanup_can_resources()
    
    print("程序退出")
